//@version=5
indicator("Larsson-Style Trend States (31/58 + ATR Gate)", shorttitle="Larsson-Style 31/58", overlay=true)

// === Inputs ===
emaFastLen   = input.int(31, "Fast EMA", minval=1)
emaSlowLen   = input.int(58, "Slow EMA", minval=1)
atrLen       = input.int(14, "ATR Length", minval=1)
atrGate      = input.float(0.25, "ATR Gate (?EMA / ATR)", step=0.05,
     tooltip="Minimum normalized distance between EMAs to avoid chop. Try 0.2?0.6.")
slopeGate    = input.float(0.0, "Slow EMA Slope Gate", step=0.0001,
     tooltip="Require slow EMA slope to be > this for UP, < -this for DOWN. 0 disables.")
useHTF       = input.bool(false, "Use Higher Timeframe Filter?")
htfTF        = input.timeframe("D", "HTF (if enabled)")
htfLen       = input.int(50, "HTF EMA Length", minval=1)

showCloud    = input.bool(true, "Show EMA Cloud?")
shadeTrend   = input.bool(true, "Background Shade by Trend?")
plotSignals  = input.bool(true, "Plot Cross Signals?")

// === Core Calculations ===
emaFast = ta.ema(close, emaFastLen)
emaSlow = ta.ema(close, emaSlowLen)

atr     = ta.atr(atrLen)
delta   = emaFast - emaSlow
norm    = atr > 0 ? delta / atr : 0.0           // normalized distance to avoid pure chop

// Slope of slow EMA (helps reduce whipsaws)
slowSlope = emaSlow - emaSlow[1]

// Optional HTF filter (simple: price vs HTF EMA)
htfEMA = useHTF ? request.security(syminfo.tickerid, htfTF, ta.ema(close, htfLen), barmerge.gaps_off, barmerge.lookahead_off) : na
htfUp  = useHTF ? close > htfEMA : true
htfDn  = useHTF ? close < htfEMA : true

// === State Logic (UP / DOWN / NEUTRAL) ===
upCond   = emaFast > emaSlow and math.abs(norm) >= atrGate and (slopeGate <= 0 or slowSlope >  slopeGate) and htfUp
downCond = emaFast < emaSlow and math.abs(norm) >= atrGate and (slopeGate <= 0 or slowSlope < -slopeGate) and htfDn
state = upCond ? 1 : downCond ? -1 : 0

// === Colors ===
colUp   = color.new(color.teal, 0)
colDn   = color.new(color.red,  0)
colNeu  = color.new(color.gray, 0)

// === Plots ===
pFast = plot(emaFast, "EMA Fast", color = state == 1 ? colUp : state == -1 ? colDn : colNeu, linewidth=2)
pSlow = plot(emaSlow, "EMA Slow", color = state == 1 ? colUp : state == -1 ? colDn : colNeu, linewidth=2)

// Cloud between EMAs (optional)  << fixed: use fill(), not plotfill
fill(pFast, pSlow,
     title="EMA Cloud",
     color = showCloud ? (state == 1 ? color.new(colUp, 80) : state == -1 ? color.new(colDn, 80) : color.new(colNeu, 85)) : na)

// Background shade by trend (optional)
bgcolor(shadeTrend ? (state == 1 ? color.new(colUp, 92) : state == -1 ? color.new(colDn, 92) : na) : na)

// === Cross Signals (optional) ===
bullCross = ta.crossover(emaFast, emaSlow)
bearCross = ta.crossunder(emaFast, emaSlow)

plotshape(plotSignals and bullCross, title="Bull Cross", style=shape.triangleup, location=location.belowbar, size=size.tiny, color=colUp, text="?")
plotshape(plotSignals and bearCross, title="Bear Cross", style=shape.triangledown, location=location.abovebar, size=size.tiny, color=colDn, text="?")

// === Alerts ===
alertcondition(bullCross, title="Bull Cross", message="Fast EMA crossed above Slow EMA")
alertcondition(bearCross, title="Bear Cross", message="Fast EMA crossed below Slow EMA")
alertcondition(state == 1,  title="Trend UP",     message="Larsson-style state: UP")
alertcondition(state == -1, title="Trend DOWN",   message="Larsson-style state: DOWN")
alertcondition(state == 0,  title="Trend NEUTRAL",message="Larsson-style state: NEUTRAL")

